home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’95
/
Hex Abacus
/
Abacus.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-24
|
8KB
|
335 lines
// Abacus.c
//
// A simple decimal and hexadecimal abacus
// Copyright © 1995 Gary Kacmarcik
// All rights reserved.
//
// Modification History:
// 22 JUN 95 GJK began at MacHack
// 24 JUN 95 GJK finished 1st version
// Magic Cap stuff
#include "Magic.h"
#include "Debug.h"
#include "Package.h"
// required by linker
main() {}
#undef CURRENTCLASS
#pragma segment Abacus
#define CURRENTCLASS Abacus
#define iHexOnly MakePackageIndexical(27,1)
#define iiAbacusBead MakePackageFlatIndexical(25)
#define iiAbacus MakePackageFlatIndexical(26)
#define NUM_ROWS 16
// height of content area of bar
#define BAR_HEIGHT 0x0C00
#define BAR_TOTAL (BAR_HEIGHT + 0x0400)
#define BAR_TOP_OFFSET (BAR_TOTAL/2)
#define BEAD_WIDTH 0x1000
#define BEAD_WIDTH_2 0x0800
#define ROW_SPACING 0x0400
Method void
Abacus_SetEndian(ObjectID self, SignedShort value)
{
SetField(self, endian, value);
DirtyContent(iiAbacus);
RedrawNow();
}
Method void
Abacus_SetBase(ObjectID self, SignedShort value)
{
SetField(self, base, 0);
PlaySound(iMagicSound);
}
Method void
Abacus_Draw(ObjectID self, ObjectID canvas, ObjectID clip)
{
Box bBox, bLine;
Dot dOrigin;
Dot where;
Abacus_Fields fields;
int i,j;
int nOn, nOff;
int nRowValue;
ObjectID idText;
TextRange range;
InheritedDraw(self, canvas, clip);
// get a ptr to our fields so that we can access them more directly
ReadFields(self, &fields);
ContentBox(self, &bBox);
EraseBox(canvas, clip, &bBox);
// draw the wires
bLine.top = bBox.top;
bLine.bottom = bBox.bottom;
bLine.left = bBox.left + BEAD_WIDTH_2 + ROW_SPACING - 0x0100;
for (i=0; i<NUM_ROWS; i++)
{
bLine.right = bLine.left + 0x0300;
FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
bLine.left += BEAD_WIDTH + ROW_SPACING;
}
// draw the horizontal middle bar
bLine.top = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET;
bLine.bottom = bLine.top + 0x0100;
bLine.left = bBox.left;
bLine.right = bBox.right;
FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
bLine.top += 0x0100;
bLine.bottom = bLine.top + 0x0100;
FillBox(canvas, clip, &bLine, rgbWhite, pixelCopy);
bLine.top += 0x0100;
bLine.bottom = bLine.top + BAR_HEIGHT;
FillBox(canvas, clip, &bLine, rgbLtGray, pixelCopy);
bLine.top += BAR_HEIGHT;
bLine.bottom = bLine.top + 0x0100;
FillBox(canvas, clip, &bLine, rgbDkGray, pixelCopy);
bLine.top += 0x0100;
bLine.bottom = bLine.top + 0x0100;
FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
// draw the hex digits in the bar
idText = NewTransient(Text_, nil);
ReplaceTextWithString(idText, "\p0123456789ABCDEF");
where.h = bBox.left + BEAD_WIDTH_2 + ROW_SPACING - 0x0200;
where.v = (bBox.top + bBox.bottom + Ascent(iFatCaps10))/2 - 0x0100;
range.length = 1;
for (i=0; i<NUM_ROWS; i++)
{
range.start = (&fields.row0)[fields.endian ? i : NUM_ROWS-1-i];
TextDraw(idText, &range, iFatCaps10, canvas, clip, &where);
where.h += BEAD_WIDTH + ROW_SPACING;
}
Destroy(idText);
// draw the beads
dOrigin.h = bBox.left + BEAD_WIDTH_2 + ROW_SPACING;
for (i=0; i<NUM_ROWS; i++)
{
nRowValue = (&fields.row0)[fields.endian ? i : NUM_ROWS-1-i];
// each "on" top bead is worth 4
nOn = (nRowValue & 0x000C) >> 2;
nOff = 4 - nOn;
// draw "off" top beads
// start from top and work down
dOrigin.v = bBox.top + BEAD_WIDTH_2;
for (j=0; j<nOff; j++)
{
DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
dOrigin.v += BEAD_WIDTH;
}
// draw "on" top beads
// start from top of middle bar and work up
dOrigin.v = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET - BEAD_WIDTH_2;
for (j=0; j<nOn; j++)
{
DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
dOrigin.v -= BEAD_WIDTH;
}
// each "on" bottom bead is worth 1
nOn = nRowValue & 0x0003;
nOff = 4 - nOn;
// draw "off" bottom beads
// start from bottom and work up
dOrigin.v = bBox.bottom - BEAD_WIDTH_2;
for (j=0; j<nOff; j++)
{
DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
dOrigin.v -= BEAD_WIDTH;
}
// draw "on" bottom beads
// start from bottom of middle bar and work down
dOrigin.v = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET + BAR_TOTAL + BEAD_WIDTH_2;
for (j=0; j<nOn; j++)
{
DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
dOrigin.v += BEAD_WIDTH;
}
// next row
dOrigin.h += BEAD_WIDTH + ROW_SPACING;
}
}
Method void
Abacus_Touch(ObjectID self, ObjectID touchInput)
{
Dot where;
Box bBox;
int i;
int nTappedRow;
int nRowLeft, nRowRight;
int nBarTop;
int nValue;
int nBeadNum;
Abacus_Fields *fields;
// find where the tap was
LatestPoint(touchInput, &where);
ContentBox(self, &bBox);
fields = BeginModifyFields(self);
// first, find which row (if any) the tap was on
nTappedRow = -1;
nRowLeft = bBox.left + ROW_SPACING;
nRowRight = nRowLeft + BEAD_WIDTH;
for (i=0; i<NUM_ROWS; i++)
{
if (where.h > nRowLeft && where.h < nRowRight)
nTappedRow = i;
nRowLeft += BEAD_WIDTH + ROW_SPACING;
nRowRight = nRowLeft + BEAD_WIDTH;
}
// bail if the tap wasn't within a row
if (nTappedRow == -1)
return;
if (!fields->endian)
nTappedRow = NUM_ROWS-1 - nTappedRow;
// was the click in the horizontal bar?
nBarTop = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET;
if (where.v > nBarTop && where.v < (nBarTop + BAR_TOTAL))
// we don't do anything here
return;
nValue = (&fields->row0)[nTappedRow];
if ((nBeadNum = FindBead(where, nValue, &bBox)) != -1)
{
int nNewValue;
int nOnBeads;
ObjectID idSound;
// check upper beads
if (nBeadNum < 4)
{
nOnBeads = (nValue & 0x000C) >> 2;
if ((4-nOnBeads) > nBeadNum)
{ // down (adding 4-beads)
idSound = iMagicSound;
nNewValue = nValue + 4 * (4-nOnBeads-nBeadNum);
}
else
{ // up (subtracting 4-beads)
idSound = iMagicSound;
nNewValue = nValue - 4 * (nOnBeads+nBeadNum-3);
}
}
// check lower beads
else
{
nOnBeads = (nValue & 0x0003);
nBeadNum -= 4;
if (nOnBeads > nBeadNum)
{ // down (subtracting 1-beads)
idSound = iMagicSound;
nNewValue = nValue - (nOnBeads-nBeadNum);
}
else
{ // up (adding 1-beads)
idSound = iMagicSound;
nNewValue = nValue + (4-nOnBeads+nBeadNum-3);
}
}
UpdateRow(fields, nTappedRow, nNewValue);
// PlaySound(idSound);
DirtyContent(self);
RedrawNow();
}
EndModify(self);
}
// update the row with the given value
// this assumes that it is called in a BeginModifyFields/EndModify struct
// make sure that the carry beads get propagated
Private void
UpdateRow(Abacus_Fields *fields, int row, int newVal)
{
(&fields->row0)[row] = (newVal & 0x000F);
for (row++; newVal > 0x000F && row < NUM_ROWS; row++)
{
newVal = (&fields->row0)[row] + 1;
(&fields->row0)[row] = (newVal & 0x000F);
}
}
// given a point and a row, this will return the bead that contains the point.
Private int
FindBead(Dot where, int nValue, Box *bBox)
{
int nTapOnTopPart;
int nBeadTop;
int nTop, nBottom;
int nBase;
int i,j;
// was the click in the upper or lower portion of the abacus?
if (where.v < (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET)
{
nTapOnTopPart = 1;
nTop = bBox->top;
nBottom = (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET;
nValue = (nValue & 0x000C) >> 2;
nBase = 0;
}
else
{
nTapOnTopPart = 0;
nTop = (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET + BAR_TOTAL;
nBottom = bBox->bottom;
// we reverse the "on" and "off" bits here so that the following
// calculations can be shared with the upper part
nValue = 4 - (nValue & 0x0003);
nBase = 4;
}
// at this point, nValue contains the # of beads at the bottom part of
// the tapped section of the abacus. for the upper part, this corresponds
// to "on" beads, for the lower part, this corresponds to "off" beads.
// check the upper beads
nBeadTop = nTop;
for (i=0; i<(4-nValue) ;i++, nBeadTop += BEAD_WIDTH)
if (where.v > nBeadTop && where.v < nBeadTop + BEAD_WIDTH)
return nBase + i;
// check the lower beads
nBeadTop = nBottom - BEAD_WIDTH;
for (i=3, j=0; j<nValue; i--, j++, nBeadTop -= BEAD_WIDTH)
if (where.v > nBeadTop && where.v < nBeadTop + BEAD_WIDTH)
return nBase + i;
return -1;
}
#undef CURRENTCLASS